// 14 重载运算与类型转换
/**
 * 当运算符被用于类类型的对象时，C++语言允许我们为其指定新的含义；同时，我们也能自定义类类型之间的转换规则。和内置类型的转换一样，类类型转换隐式地将一种类型的对象转换成另一种我们所需类型的对象。
 * 当运算符作用于类类型的运算对象时，可以通过运算符重载重新定义该运算符的含义。明智地使用运算符重载能令我们的程序更易于编写和阅读。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;
#include "../Chapter13/13.5.cc" // 不能编译，因为重复定义的main函数
#include "../Chapter12/12.1.6.cc" // 不能编译，因为重复定义的main函数
#include <functional>

class SmallInt
{
public:
    // 如果构造函数只接受一个实参，则它实际上定义了转换为此类类型的隐式转换机制，又是我们把这种构造函数称作转换构造函数（converting constructor）。
    SmallInt(int i=0) : val(i)
    {
        if(i<0 || i>255)
            throw std::out_of_range("Bad SmallInt value");
    }
    //operator int() const { return val;} // 类型转换运算符
    explicit operator int() const { return val; } // 编译器不会自动执行这一类型转换
private:
    std::size_t val;
};

int main()
{
    // 14.9 重载、类型转换与运算符
    // 在7.5.4节（第263页）中我们看到由一个实参调用的非显式构造函数定义了一种隐式的类型转换，这种构造函数将实参类型的对象转换成类类型。
    // 我们同样能定义对于类类型的类型转换，通过定义类型转换运算符可以做到这一点。
    // 转换构造函数和类型转换运算符共同定义了类类型转换（class-type conversions），这样的转换有时也被称作用户定义的类型转换（user-defined conversions）。

    // 14.9.1 类型转换运算符
    // 类型转换运算符（conversion operator）是类的一种特殊成员函数，它负责将一个类类型的值转换成其他类型。类型转换函数的一般形式如下所示：
    //operator type() const;
    // 其中type表示某种类型。类型转换运算符可以面向任意类型（除了void之外）进行定义，只要该类型能作为函数的返回类型（参见6.1节，第184页）。
    // 因此，我们不允许转换成数组或者函数类型，但允许转换成指针（包括数组指针及函数指针）或者引用类型。
    // 类型转换运算符既没有显式的返回类型，也没有形参，而且必须定义成类的成员函数。类型转换运算符通常不应该改变待转换对象的内容，因此，类型转换运算符一般被定义成const成员。
    // 一个类型转换函数必须是类的成员函数；它不能声明返回类型，形参列表也必须为空。类型转换函数通常应该是const。

    // 定义含有类型转换运算符的类
    // 如果构造函数只接受一个实参，则它实际上定义了转换为此类类型的隐式转换机制，又是我们把这种构造函数称作转换构造函数（converting constructor）。
    // 我们的SmallInt类既定义了向类类型的转换，也定义了从类类型向其他类型的转换。其中，构造函数将算术类型的值转换成SmallInt对象，而类型转换运算符将SmallInt对象转换成int：
    SmallInt si;
    si = 4; // 首先将4隐式地转换成SmallInt（可参见，7.5.4　隐式的类类型转换），然后调用SmallInt::operator=
    si + 3; // 首先将si隐式地转换成int，然后执行整数的加法
    // 尽管编译器一次只能执行一个用户定义的类型转换（参见4.11.2节，第144页），但是隐式的用户定义类型转换可以置于一个标准（内置）类型转换之前或之后（参见4.11.1节，第141页），并与其一起使用。
    // 因此，我们可以将任何算术类型传递给SmallInt的构造函数。类似的，我们也能使用类型转换运算符将一个SmallInt对象转换成int，然后再将所得的int转换成任何其他算术类型：
    SmallInt si1 = 3.14; // 内置类型转换将double实参转换成int。调用SmallInt(int)构造函数。
    si1 + 3.14;          // SmallInt的类型转换运算符将si1转换成int。内置类型转换将所得的int继续转换成double。
    // 同时，尽管类型转换函数不负责指定返回类型，但实际上每个类型转换函数都会返回一个对应类型的值：

    // 提示：避免过度使用类型转换函数
    // 和使用重载运算符的经验一样，明智地使用类型转换运算符也能极大地简化类设计者的工作，同时使得使用类更加容易。
    // 然而，如果在类类型和转换类型之间不存在明显的映射关系，则这样的类型转换可能具有误导性。
    // 例如，假设某个类表示Date，我们也许会为它添加一个从Date到int的转换。然而，类型转换函数的返回值应该是什么？
    // 一种可能的解释是，函数返回一个十进制数，依次表示年、月、日，例如，July 30，1989可能转换为int值19890730。
    // 同时还存在另外一种合理的解释，即类型转换运算符返回的int表示的是从某个时间节点（比如January 1，1970）开始经过的天数。
    // 显然这两种理解都合情合理，毕竟从形式上看它们产生的效果都是越靠后的日期对应的整数值越大，而且两种转换都有实际的用处。
    // 问题在于Date类型的对象和int类型的值之间不存在明确的一对一映射关系。
    // 因此在此例中，不定义该类型转换运算符也许会更好。作为替代的手段，类可以定义一个或多个普通的成员函数以从各种不同形式中提取所需的信息。

    // 类型转换运算符可能产生意外结果
    // 在实践中，类很少提供类型转换运算符。在大多数情况下，如果类型转换自动发生，用户可能会感觉比较意外，而不是感觉受到了帮助。
    // 然而这条经验法则存在一种例外情况：对于类来说，定义向bool的类型转换还是比较普遍的现象。

    // 显示的类型转换运算符
    // 为了防止这样的异常情况发生，C++11新标准引入了显式的类型转换运算符（explicit conversion operator）：
    //explicit operator int() const { return val; } // 参见上面定义的的SmallInt
    // 和显式的构造函数（参见7.5.4节，第265页）一样，编译器（通常）也不会将一个显式的类型转换运算符用于隐式类型转换：
    SmallInt si2 = 3; // 正确，SmallInt的构造函数不是显示的
    si2 + 3;          // 错误，此处需要隐式的类型转换，但类型转换运算符是显示的
    static_cast<int>(si2) + 3; // 正确，显示地请求类型转换
    // 当类型转换运算符是显式的时，我们也能执行类型转换，不过必须通过显式的强制类型转换才可以。
    // 该规定存在一个例外，即如果表达式被用作条件，则编译器会将显式的类型转换自动应用于它。换句话说，当表达式出现在下列位置时，显式的类型转换将被隐式地执行：
    // · if、while及do语句的条件部分
    // · for语句头的条件表达式
    // · 逻辑非运算符（！）、逻辑或运算符（||）、逻辑与运算符（&&）的运算对象
    // · 条件运算符（？ ：）的条件表达式。

    // 转换为bool
    // 在标准库的早期版本中，IO类型定义了向void＊的转换规则，以求避免上面提到的问题。
    // 在C++11新标准下，IO标准库通过定义一个向bool的显式类型转换实现同样的目的。无论我们什么时候在条件中使用流对象，都会使用为IO类型定义的operator bool。例如：
    //while(std::cin >> value)
    // while语句的条件执行输入运算符，它负责将数据读入到value并返回cin。为了对条件求值，cin被istream operator bool类型转换函数隐式地执行了转换。
    // 如果cin的条件状态是good（参见8.1.2节，第280页），则该函数返回为真；否则该函数返回为假。
    // 向bool的类型转换通常用在条件部分，因此operator bool一般定义成explicit的。




    return 0;
}